У попередньому завданні вам потрібно було створити:
карту, котра відображає рівень приросту / скорочення населення за регіонами станом на останній доступний рік;
лінійний графік, котрий відображає рівень приросту / скорочення населення за регіонами за всіма роками (одна лінія на графіку - один регіон).
Для цього завдання вам потрібно налагодити інтеракцію між картою та графіком.
При наведенні на якийсь регіон на карті він має виділятися на графіку за допомогою товщини лінії та її кольору.
При наведенні на якусь лінію на графіку відповідний їй регіон має виділятися на карті за допомогою прозорості.
import altair as alt
import pandas as pd
import geopandas as gpd
ukraine = gpd.read_file('ukraine.json')
ukraine.head()
| GID_0 | NAME_0 | GID_1 | NAME_1 | VARNAME_1 | NL_NAME_1 | TYPE_1 | ENGTYPE_1 | CC_1 | HASC_1 | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | UKR | Ukraine | UKR.1_1 | Cherkasy | Cherkas'ka Oblast'|Cherkasskaya Oblast'|Cherkassy | None | Oblast' | Region | None | UA.CK | MULTIPOLYGON (((31.32614 48.74507, 31.31716 48... |
| 1 | UKR | Ukraine | UKR.2_1 | Chernihiv | Chernigov|Tschernigow | None | Oblast' | Region | None | UA.CH | MULTIPOLYGON (((33.09283 50.50966, 33.09261 50... |
| 2 | UKR | Ukraine | UKR.3_1 | Chernivtsi | Chernivets'ka Oblast'|Chernovitskaya Oblast'|C... | None | Oblast' | Region | None | UA.CV | MULTIPOLYGON (((24.93280 47.72794, 24.93301 47... |
| 3 | UKR | Ukraine | UKR.4_1 | Crimea | Crimée|Criméia|Krim|Krymskaya Respublika|Respu... | None | Autonomous Republic | Autonomous Republic | None | UA.KR | MULTIPOLYGON (((33.79291 44.39153, 33.79465 44... |
| 4 | UKR | Ukraine | UKR.5_1 | Dnipropetrovs'k | Dnipropetrovsk|Dniepropietrovsk|Dnjepropetrowsk | None | Oblast' | Region | None | UA.DP | MULTIPOLYGON (((33.93176 47.48407, 33.92332 47... |
ukraine = ukraine[['NAME_1', 'geometry']]
ukraine.columns = ['region', 'geometry']
ukraine.head()
| region | geometry | |
|---|---|---|
| 0 | Cherkasy | MULTIPOLYGON (((31.32614 48.74507, 31.31716 48... |
| 1 | Chernihiv | MULTIPOLYGON (((33.09283 50.50966, 33.09261 50... |
| 2 | Chernivtsi | MULTIPOLYGON (((24.93280 47.72794, 24.93301 47... |
| 3 | Crimea | MULTIPOLYGON (((33.79291 44.39153, 33.79465 44... |
| 4 | Dnipropetrovs'k | MULTIPOLYGON (((33.93176 47.48407, 33.92332 47... |
df = pd.read_csv('population_trends.csv')
df = df[df.region != 'Ukraine']
df.head()
| region | year | rate | |
|---|---|---|---|
| 31 | Crimea | 1989 | 3.9 |
| 32 | Crimea | 1990 | 2.5 |
| 33 | Crimea | 1991 | 0.9 |
| 34 | Crimea | 1992 | -0.7 |
| 35 | Crimea | 1993 | -2.7 |
df_map_2019 = f = df.loc[df.year == 2019]
df_map_2019.head()
| region | year | rate | |
|---|---|---|---|
| 61 | Crimea | 2019 | NaN |
| 92 | Vinnytsya | 2019 | -7.9 |
| 123 | Volyn | 2019 | -2.8 |
| 154 | Dnipropetrovs'k | 2019 | -8.9 |
| 185 | Donets'k | 2019 | NaN |
Щоб зробити графіки інтерактивними, я створила два селектори :
select_line (виділятиме лінію, якщо навести курсор на місто на карті) select_region(виділятиме регіон на карті, якщо навести курсор на лінію)Для виділення ліній я зробила заначення empty = 'none', щоб по замовчуванню лінії не були яскравими і не виділялися.
select_line = alt.selection_single(on = 'mouseover', fields = ['region'], nearest = False, empty = 'none')
select_region = alt.selection_single(on = 'mouseover', fields = ['region'], nearest = False, empty = 'all')
Створення графіків майже таке саме як у попередньому домашньому завданні.
Прозорість карти України буде залежати від select_region :
opacity = alt.condition(select_region,
alt.value(1.0),
alt.value(0.2))
За замовчеванням вся карта - виділена (непрозора), при наведенні на лінію, все крім вибраного регіону стає прозорішим.
Щоб на графіку ukraine_rate спрацьовував Tooltip з двох шарів (base i rate_2019), я додала add_selection(alt.selection_single()) до шару rate_2019. Без цього Tooltip чомусь не працював.
base = alt.Chart(ukraine).mark_geoshape(fill = 'lightgrey', stroke = 'grey'
).encode(
tooltip = [alt.Tooltip('region:N')],
opacity = alt.condition(select_region,
alt.value(1.0),
alt.value(0.2)
),)
rate_2019 = alt.Chart(ukraine,
title={"text": ["Population growth rate in 2019"],
"subtitle": ["hover over the required area for more information"],
"subtitleColor": '#333333'}
).transform_lookup(
lookup = 'region',
from_ = alt.LookupData(data = df_map_2019,
key = 'region',
fields=['region', 'rate'])
).mark_geoshape(stroke = 'grey').encode(
color = alt.Color('rate:Q',
scale = alt.Scale(
domain=[-12, -8, -4, 0],
range=['#de2d26', '#fc9272', '#fee0d2', 'white'],
type='linear'
),
),
opacity = alt.condition(
select_region,
alt.value(1.0),
alt.value(0.2)
),
tooltip = [
alt.Tooltip('region:N'),
alt.Tooltip('rate:Q')
]
).add_selection(alt.selection_single())
ukraine_rate = alt.layer(base, rate_2019).add_selection(select_line)
Лінії виділяються за допомогою товщони і кольору. При селекшені стають червоними і товстішими. За замовчеванням всі сірого кольору.
Мінус такого представлення - на лінію досить важко навести курсор.
line_chart = alt.Chart(df,
title={"text": ["Population growth rate for all years"],
"subtitle": ["hover over the line for more information"],
"subtitleColor": '#333333'}
).mark_line().encode(
x = alt.X('year:Q',
scale = alt.Scale(domain = [1988, 2020], nice = False),
axis = alt.Axis(title = None, format = 'f')),
y = alt.Y('rate:Q', title = 'rate'),
detail = alt.Detail('region:N'),
color = alt.condition(
select_line,
alt.value('#de2d26'),
alt.value('grey')
),
size = alt.condition(
select_line,
alt.value(2.5),
alt.value(1.5)
),
tooltip = [
alt.Tooltip('region:N'),
alt.Tooltip('year:Q'),
alt.Tooltip('rate:Q')
]
).add_selection(select_region)
alt.hconcat(ukraine_rate.properties(width = 600, height = 400),
line_chart.properties(width = 600, height = 400)
).properties(background = '#F9F9F9',
padding = 25
).configure_title(font = 'Times',
fontSize = 18,
dy = -5
).configure_axis(gridDash = [2, 2],
titleFont = 'Times',
labelFont = 'Times',
titleFontSize = 13,
labelFontSize = 12
).configure_view(strokeWidth = 0
).configure_legend(orient='none',
legendX=0, legendY=350,
direction='horizontal',
titleFont = 'Times',
labelFont = 'Times',
titleFontSize = 13,
labelFontSize = 12,
gradientLength=200,
)